library ieee;
use ieee.std_logic_1164.all;
use IEEE.NUMERIC_STD.all;
use work.typeDefinitions.all;

entity ForwardingLogic is
  port
    (
      forwardOut   : out forwardSignalsIn;
      forwardIn    : in  forwardSignalsOut;
      instructions : in  interStageOperation

      );
end ForwardingLogic;

architecture ForwardingLogic_arch of ForwardingLogic is

begin
  -- notes: for LW followed by BEQ/BNE, stalling is important as hell (stall+bubble pipeline until LW gets to WB stage)

  process(forwardIn, instructions)
  begin

    -- default to no forwarding
    forwardOut.id.busA <= x"00000000";
    forwardOut.id.selA <= '0';
    -- for forwarding to ID stage R[rs]
    if(forwardIn.id.rs /= "00000") then
      -- check forwarding for all instructions except LUI, J, JAL, HALT
      case instructions.decode is
        when o_addu | o_and | o_jr | o_nor | o_or | o_slt | o_sltu | o_sll | o_srl | o_subu | o_xor | o_addiu | o_andi | o_beq | o_bne | o_lw | o_ori | o_slti | o_sltiu | o_sw | o_xori | o_ll | o_sc =>
          if(forwardIn.mem.destination = forwardIn.id.rs and forwardIn.id.rs /= "00000") then
                                        -- if mem dest == fwdSignals.id.rs, forward aluresult to R[rs] output
                                        -- (be sure to stall for LW, others?)
            forwardOut.id.busA <= forwardIn.mem.aluResult;
            forwardOut.id.selA <= '1';
          elsif(forwardIn.wb.destination = forwardIn.id.rs and forwardIn.id.rs /= "00000") then
                                        -- else if wb dest == fwdSignals.id.rs, forward wdata to R[rs] output
                                        -- (should occur for LW ONLY when LW is in WB stage)
            forwardOut.id.busA <= forwardIn.wb.writeData;
            forwardOut.id.selA <= '1';
          end if;
        when others =>
                                        -- do not forward
      end case;
    end if;

    -- default to no forwarding
    forwardOut.id.busB <= x"00000000";
    forwardOut.id.selB <= '0';
    -- for forwarding to ID stage R[rt]
    if(forwardIn.id.rt /= "00000") then
      case instructions.decode is
                                        -- check forwarding for all instructios except JR, SLL, SRL, LUI, J, JAL, HALT, and all immediates but SW/branches
        when o_addu | o_and | o_nor | o_or | o_slt | o_sltu | o_subu | o_xor | o_beq | o_bne | o_sw | o_sc  =>
          if(forwardIn.mem.destination = forwardIn.id.rt and forwardIn.id.rt /= "00000") then
                                        -- if MEM dest == fwdSignals.id.rt, forward aluresult to R[rt] output
                                        -- (be sure to stall for LW, others?)
            forwardOut.id.busB <= forwardIn.mem.aluResult;
            forwardOut.id.selB <= '1';
          elsif(forwardIn.wb.destination = forwardIn.id.rt and forwardIn.id.rt /= "00000") then
                                        -- else if WB dest == fwdSignals.id.rt, forward wdata to R[rt] output
                                        -- (should occur for LW ONLY when LW is in WB stage)
            forwardOut.id.busB <= forwardIn.wb.writedata;
            forwardOut.id.selB <= '1';
          end if;
        when others =>
                                        -- do not forward
      end case;
    end if;

    -- default to no forwarding
    forwardOut.exec.busA <= x"00000000";
    forwardout.exec.selA <= '0';
    -- for forwarding to EX stage R[rs]
    if(forwardIn.exec.rs /= "00000") then
      -- check forwarding for all instructions except BEQ, BNE, JR, LUI, J, JAL, HALT
      -- beq/bne/jr not needed b/c decided in id stage
      case instructions.id is
        when o_addu | o_and | o_jr | o_nor | o_or | o_slt | o_sltu | o_sll | o_srl | o_subu | o_xor | o_addiu | o_andi | o_lw | o_ori | o_slti | o_sltiu | o_sw | o_xori | o_ll | o_sc =>
          if(forwardIn.mem.destination = forwardIn.exec.rs and forwardIn.exec.rs /= "00000") then
                                        -- if mem dest == fwdSignals.exec.rs, forward aluresult to ALU's A input
                                        -- (be sure to stall for LW, others?)
            forwardOut.exec.busA <= forwardIn.mem.aluResult;
            forwardOut.exec.selA <= '1';
          elsif(forwardIn.wb.destination = forwardIn.exec.rs and forwardIn.exec.rs /= "00000") then
                                        -- else if wb dest == fwdSignals.exec.rs, forward wdata to ALU's A input
                                        -- (should not occur for LW iff pipeline stalled until LW gets to WB stage)
            forwardOut.exec.busA <= forwardIn.wb.writeData;
            forwardOut.exec.selA <= '1';
          elsif(forwardIn.wb2.destination = forwardIn.exec.rs and forwardIn.exec.rs /= "00000") then
            forwardOut.exec.busA <= forwardIn.wb2.writeData;
            forwardOut.exec.selA <= '1';
          end if;
        when others =>
                                        -- do not forward
      end case;
    end if;

    -- default to no forwarding
    forwardOut.exec.busB <= x"00000000";
    forwardOut.exec.selB <= '0';
    -- for forwarding to EX stage R[rt]
    if(forwardIn.exec.rt /= "00000") then
      -- check forwarding for all instructions except BEQ, BNE, JR, LUI, J, JAL, HALT, SLL, SRL, and all immediates but SW/branches
      -- beq/bne not needed b/c decided in id stage
      case instructions.id is
        when o_addu | o_and | o_nor | o_or | o_slt | o_sltu | o_subu | o_xor | o_sw | o_sc =>
          if(forwardIn.mem.destination = forwardIn.exec.rt and forwardIn.exec.rt /= "00000") then
                                        -- if MEM dest == fwdSignals.exec.rt, forward aluresult to ALU's B input
                                        -- (be sure to stall for LW, others?)
            forwardOut.exec.busB <= forwardIn.mem.aluResult;
            forwardOut.exec.selB <= '1';
          elsif(forwardIn.wb.destination = forwardIn.exec.rt and forwardIn.exec.rt /= "00000") then
                                        -- else if WB dest == fwdSignals.exec.rt, forward wdata to ALU's B input
                                        -- (should not occur for LW iff pipeline stalled until LW gets to WB stage)
            forwardOut.exec.busB <= forwardIn.wb.writeData;
            forwardOut.exec.selB <= '1';
          elsif(forwardIn.wb2.destination = forwardIn.exec.rt and forwardIn.exec.rt /= "00000") then
            forwardOut.exec.busB <= forwardIn.wb2.writeData;
            forwardOut.exec.selB <= '1';
          end if;
        when others =>
                                        -- do not forward
      end case;
    end if;

    -- defaults
    forwardOut.mem.busB <= x"00000000";
    forwardOut.mem.selB <= '0';
    -- for forwarding to MEM stage R[rt] on SW's
    -- exec interstage register
    if(instructions.exec = o_sw or instructions.exec = o_sc) then
      if(forwardIn.wb.destination = forwardIn.mem.rt and forwardIn.mem.rt /= "00000") then
                                        -- if reg# being stored in regfile == reg# being written to mem, forward wb data to data input of mem
        forwardOut.mem.busB <= forwardIn.wb.writeData;
        forwardOut.mem.selB <= '1';
      elsif(forwardIn.wb2.destination = forwardIn.mem.rt and forwardIn.mem.rt /= "00000") then
        forwardOut.mem.busB <= forwardIn.wb2.writeData;
        forwardOut.mem.selB <= '1';
      end if;
    end if;

  end process;
  
end ForwardingLogic_arch;
